Add FP8/FP4 block-quantized support to GatherBlockQuantized contrib op - #32480
Conversation
Co-authored-by: kunal-vaishnavi <115581922+kunal-vaishnavi@users.noreply.github.com>
Co-authored-by: kunal-vaishnavi <115581922+kunal-vaishnavi@users.noreply.github.com>
Co-authored-by: kunal-vaishnavi <115581922+kunal-vaishnavi@users.noreply.github.com>
Co-authored-by: kunal-vaishnavi <115581922+kunal-vaishnavi@users.noreply.github.com>
Co-authored-by: kunal-vaishnavi <115581922+kunal-vaishnavi@users.noreply.github.com>
Co-authored-by: kunal-vaishnavi <115581922+kunal-vaishnavi@users.noreply.github.com>
Co-authored-by: kunal-vaishnavi <115581922+kunal-vaishnavi@users.noreply.github.com>
Co-authored-by: kunal-vaishnavi <115581922+kunal-vaishnavi@users.noreply.github.com>
Co-authored-by: kunal-vaishnavi <115581922+kunal-vaishnavi@users.noreply.github.com>
Co-authored-by: kunal-vaishnavi <115581922+kunal-vaishnavi@users.noreply.github.com>
Co-authored-by: kunal-vaishnavi <115581922+kunal-vaishnavi@users.noreply.github.com>
…eek-engram' into copilot/add-op-support-for-qwen-3-8-flash-next Co-authored-by: kunal-vaishnavi <115581922+kunal-vaishnavi@users.noreply.github.com>
Co-authored-by: kunal-vaishnavi <115581922+kunal-vaishnavi@users.noreply.github.com>
…ids, head_offsets Co-authored-by: kunal-vaishnavi <115581922+kunal-vaishnavi@users.noreply.github.com>
…utput Co-authored-by: kunal-vaishnavi <115581922+kunal-vaishnavi@users.noreply.github.com>
Co-authored-by: kunal-vaishnavi <115581922+kunal-vaishnavi@users.noreply.github.com>
Co-authored-by: kunal-vaishnavi <115581922+kunal-vaishnavi@users.noreply.github.com>
Co-authored-by: kunal-vaishnavi <115581922+kunal-vaishnavi@users.noreply.github.com>
Co-authored-by: kunal-vaishnavi <115581922+kunal-vaishnavi@users.noreply.github.com>
Co-authored-by: kunal-vaishnavi <115581922+kunal-vaishnavi@users.noreply.github.com>
…ids, head_offsets Co-authored-by: kunal-vaishnavi <115581922+kunal-vaishnavi@users.noreply.github.com>
…utput Co-authored-by: kunal-vaishnavi <115581922+kunal-vaishnavi@users.noreply.github.com>
Co-authored-by: kunal-vaishnavi <115581922+kunal-vaishnavi@users.noreply.github.com>
Co-authored-by: kunal-vaishnavi <115581922+kunal-vaishnavi@users.noreply.github.com>
Co-authored-by: kunal-vaishnavi <115581922+kunal-vaishnavi@users.noreply.github.com>
Co-authored-by: kunal-vaishnavi <115581922+kunal-vaishnavi@users.noreply.github.com>
Co-authored-by: kunal-vaishnavi <115581922+kunal-vaishnavi@users.noreply.github.com>
Co-authored-by: kunal-vaishnavi <115581922+kunal-vaishnavi@users.noreply.github.com>
…) support Co-authored-by: kunal-vaishnavi <115581922+kunal-vaishnavi@users.noreply.github.com>
Co-authored-by: kunal-vaishnavi <115581922+kunal-vaishnavi@users.noreply.github.com>
Co-authored-by: kunal-vaishnavi <115581922+kunal-vaishnavi@users.noreply.github.com>
…3-8-flash-next' into copilot/research-onnx-runtime-support # Conflicts: # docs/ContribOperators.md Co-authored-by: kunal-vaishnavi <115581922+kunal-vaishnavi@users.noreply.github.com>
Co-authored-by: kunal-vaishnavi <115581922+kunal-vaishnavi@users.noreply.github.com>
…3-8-flash-next' into copilot/research-onnx-runtime-support Co-authored-by: kunal-vaishnavi <115581922+kunal-vaishnavi@users.noreply.github.com>
Tianlei Wu (tianleiwu)
left a comment
There was a problem hiding this comment.
Thanks for folding this into GatherBlockQuantized rather than shipping a second contrib op, and for the previous round: the WebGPU FP path is now reachable and locale-safe, the dead scale_size field is gone, the TArray rank-8 limit has a real message, the CPU FP path regained the per-thread row cache plus an incremental scale-index update instead of a full per-element decomposition, the unrelated MLAS/engram changes are split out, and the new BFloat16 / odd-nibble FP4 row / negative-index / defaults tests close the coverage gaps. All of my earlier threads are resolved.
Requesting changes on one blocker plus one semantic change that needs an explicit decision.
Blocking
The branch does not compile — every Linux, CUDA, TensorRT, WebGPU, ARM64, and minimal leg fails on the same error in contrib_ops/cpu/quantization/gather_block_quantized.cc (memset on MLFloat16 under -Werror=class-memaccess). Inline comments on both sites. Because this aborts the build early, more issues may surface once it is fixed.
Needs a decision
Out-of-range indices now zero-fill on CPU for the pre-existing integer types too. The CPU ORT_ENFORCE is replaced by a zero-fill, the schema text "It is an error if any of the index values are out of bounds" is deleted, and InvalidIndices flips from expect-failure to expect-success. That is not FP8/FP4-scoped — already-shipped int4/uint4/uint8 models go from a hard error to silently-zero output, diverging from ONNX Gather. It does make CPU match the pre-existing CUDA/WebGPU behavior, so there is a consistency argument, but it deserves an explicit owner sign-off and a mention in the title/description rather than riding along in a dtype-extension PR. Inline comment on the schema text.
Other suggestions (non-blocking)
-
ORT_ENFORCEis used forscales-shape validation atcontrib_ops/cuda/quantization/gather_block_quantized.cc:217, two lines below anORT_RETURN_IF_NOTin the same block. PreferORT_RETURN_IF_NOTfor user-input validation so the failure comes back as aStatus. -
GatherBlockQuantizedParamgrew by threeTArray<int64_t>(192 bytes) that are passed by value intoLaunchGatherBlockQuantizedKernelon every launch, including the integer paths that never read them.scale_broadcast_axisin particular only needs a flag per axis —TArray<int32_t>or a bitmask would halve/eliminate that cost. -
docs/ContribOperators.mdloses its trailing blank line at EOF in this diff. Please confirm it was regenerated bytools/python/gen_contrib_doc.pyrather than hand-edited, otherwise the Doc Gen check will keep reverting it. -
The WGSL LUT is emitted as
const kFpDequantLut = array<f32, 256>(...)and indexed with a runtimeu32. No other shader in the repo dynamically indexes a module-scopeconstarray. Worth confirming Tint/Dawn accepts it (rather than requiring avar<private>array) once the WebGPU leg gets past the compile break. -
Test gaps:
- No FP8 case with a non-zero
block_sizewheredata_shape[quantize_axis] % block_size != 0. That is exactly the "reset the block index at every quantize-axis row boundary" case both the CUDA and CPU comments call out as the reason for the generic per-axis decomposition;FpSubRowBlockScaleuses[1, 32]withblock_size = 16, i.e. an exact multiple on a single row, so it never exercises it. - No scale broadcast on a middle axis (non-leading and non-quantize).
FpGlobalPerTensorScaleonly broadcasts axis 0.
- No FP8 case with a non-zero
Co-authored-by: kunal-vaishnavi <115581922+kunal-vaishnavi@users.noreply.github.com>
Co-authored-by: kunal-vaishnavi <115581922+kunal-vaishnavi@users.noreply.github.com>
…3-8-flash-next' into copilot/research-onnx-runtime-support # Conflicts: # docs/ContribOperators.md Co-authored-by: kunal-vaishnavi <115581922+kunal-vaishnavi@users.noreply.github.com>
Co-authored-by: kunal-vaishnavi <115581922+kunal-vaishnavi@users.noreply.github.com>
…3-8-flash-next' into copilot/research-onnx-runtime-support Co-authored-by: kunal-vaishnavi <115581922+kunal-vaishnavi@users.noreply.github.com>
Co-authored-by: kunal-vaishnavi <115581922+kunal-vaishnavi@users.noreply.github.com>
Co-authored-by: kunal-vaishnavi <115581922+kunal-vaishnavi@users.noreply.github.com>
Tianlei Wu (tianleiwu)
left a comment
There was a problem hiding this comment.
The current head addresses all substantive concerns from my previous review: the CPU half-output build failure is fixed, the out-of-range behavior is explicitly documented, CUDA validation and coverage are improved, and the WebGPU FP8/FP4 path is now registered and exercised with constant inputs. I also checked the final WebGPU lookup-table change: storing raw u32 bit patterns and bitcasting the selected runtime value avoids non-finite constant-expression failures without losing FP codes. The reported CI matrix is green. One non-blocking generated-doc whitespace cleanup remains inline.
Description
This PR extends
GatherBlockQuantized(schema + CPU/CUDA/WebGPU kernels) to support FP8 and FP4 block-quantizeddatain addition to its existing integer formats.Key semantic additions and fixes:
float8e4m3fn,float8e4m3fnuz,float8e5m2,float8e5m2fnuz, andfloat4e2m1data. For these types,bitsis ignored andzero_pointsis omitted.block_size = 0for FP8/FP4, meaning the fullquantize_axisdimension is one block.quantize_axis.Gather; it avoids provider-dependent behavior and preserves asynchronous CUDA execution without stream synchronization. Op-owner sign-off is requested for this contract change.block_size = 128,quantize_axis = 1) and validates axes, ranks, scale shapes, empty outputs, launch dimensions, and integerbitsvalues.bitsduring graph resolution;bitsremains intentionally ignored for FP8/FP4.Tests cover CPU/CUDA/WebGPU behavior, defaults, BFloat16 output, packed FP4 row boundaries, negative axes and indices, invalid indices, empty outputs, and invalid attributes. Generated contrib operator/kernel documentation is included.
Motivation and Context
Qwen3.8-Flash / Qwen4-Exp requires an n-gram embedding gather over a block-quantized FP8/FP4 table. Extending
GatherBlockQuantizedkeeps a single Gather-with-quantization op while adding the dtype and broadcast-scale support required by that table.